package com.fast.common.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpException;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fast.common.mapper.FastBaseMapper;
import com.fast.common.param.BaseParam;
import com.fast.common.result.ApiCode;
import com.fast.common.service.FastConstVal;
import com.fast.common.service.IBaseService;
import com.google.common.base.CaseFormat;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * Class BaseServiceImpl
 *
 * @Auther: lhy
 * @Date: 2021/11/19 17:34
 * @Mail: 190643201@qq.com
 */

public class BaseServiceImpl<M extends FastBaseMapper<T>, T> extends ServiceImpl<M, T> implements IBaseService<T> {
    private String join;
    private String foreign;
    private String sqlSegment;
    private FastWrapper<T> fastWrapper;
    private List<IBaseService<?>> iBaseServiceList;
    private String key;
    final private String tableName = getTableName();


    @Autowired
    HttpServletRequest httpServletRequest;

    @Override
    public FastWrapper<T> select(String... column) {
        getFastWrapper().select(column);
        return fastWrapper;
    }


    public FastWrapper<T> getFastWrapper() {
        if (fastWrapper == null) {
            fastWrapper = new FastWrapper<>(this);
        }
        return fastWrapper;
    }


    @Override
    public String getTableName() {
        return this.getEntityClass().getAnnotation(TableName.class).value();
    }

    @Override
    public IBaseService<T> setForeignKey(String foreignKey) {
        foreign = foreignKey;
        return this;
    }

    @Override
    public String getForeign() {
        return this.tableName + FastConstVal.DOT + foreign;
    }

    @Override
    public IBaseService<T> setLeft() {
        join = FastConstVal.LEFT;
        return this;
    }

    @Override
    public IBaseService<T> setRight() {
        join = FastConstVal.RIGHT;
        return this;
    }

    @Override
    public String getJoin() {
        return join;
    }

    @Override
    public IBaseService<T> with(IBaseService<?> baseService, String key) {
        if (baseService.getJoin() != null) {
            sqlSegment = baseService.getJoin() + FastConstVal.SQL_JOIN
                    + baseService.getTableName() + FastConstVal.SQL_ON
                    + getColumn(key) + FastConstVal.SQL_EQUAL
                    + baseService.getForeign();
        } else {
            getIBaseServiceList().add(baseService.setKey(key));
        }
        return this;
    }

    @Override
    public List<Map<String, Object>> listMap() {
        List<Map<String, Object>> result = fastWrapper == null ?
                baseMapper.query(tableName, sqlSegment) :
                baseMapper.queryWrapper(tableName, sqlSegment, fastWrapper);
        return whetherIn(result);
    }

    @Override
    public IPage<Map<String, Object>> pageMap() {
        BaseParam baseParam = new BaseParam();
        Long p = ObjectUtil.isNull(httpServletRequest.getParameter(FastConstVal.PAGE)) ?
                baseParam.getPage() :
                Long.valueOf(httpServletRequest.getParameter(FastConstVal.PAGE));
        Long l = ObjectUtil.isNull(httpServletRequest.getParameter(FastConstVal.LIMIT)) ?
                baseParam.getLimit() :
                Long.valueOf(httpServletRequest.getParameter(FastConstVal.LIMIT));
        IPage<T> page = new Page<>(p, l);

        IPage<Map<String, Object>> result = fastWrapper == null ?
                baseMapper.pageQuery(page, tableName, sqlSegment) :
                baseMapper.pageQueryWrapper(page, tableName, sqlSegment, fastWrapper);
        result.setRecords(whetherIn(result.getRecords()));
        return result;
    }

    @Override
    public Map<String, Object> find() {
        Map<String, Object> result = fastWrapper == null ? baseMapper.find(tableName, sqlSegment)
                : baseMapper.findWrapper(tableName, sqlSegment, fastWrapper);
        return whetherIn(result);
    }

    @Override
    public IBaseService<?> setKey(String key) {
        this.key = key;
        return this;
    }

    @Override
    public String getKey() {
        return key;
    }

    @Override
    public void delete() {
        if (fastWrapper == null) throw new HttpException(ApiCode.FAIL_MISSING_ARGUMENT.getMsg());
        boolean result = remove(fastWrapper);
        fastWrapper.clear();
        if (!result) throw new HttpException(ApiCode.FAIL_NOT_METHOD.getMsg());
    }

    private String getColumn(String column) {
        if (!column.contains(FastConstVal.DOT)) {
            return tableName + FastConstVal.DOT + column;
        } else {
            return column;
        }
    }

    private Map<String, Object> whetherIn(Map<String, Object> result) {
        if (iBaseServiceList != null && result != null) {
            for (IBaseService<?> iBaseService : iBaseServiceList) {
                String foreignKey = iBaseService.getForeign();
                String pk = iBaseService.getKey();
                if (pk.contains(FastConstVal.DOT)) {
                    pk = pk.split(FastConstVal.REG_DOT, 2)[1];
                }
                pk = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, pk);
                Object keyValue = result.get(pk);
                if (keyValue == null) continue;
                Map<String, Object> withResult = iBaseService.select().eq(foreignKey, keyValue).end().find();
                if (withResult != null) result.putAll(withResult);
            }
            iBaseServiceList.clear();
        }
        if (fastWrapper != null) fastWrapper.clear();
        return result;
    }

    private List<Map<String, Object>> whetherIn(List<Map<String, Object>> result) {
        if (iBaseServiceList != null && result != null) {
            for (IBaseService<?> iBaseService : iBaseServiceList) {
                String foreignKey = iBaseService.getForeign();
                String pk = iBaseService.getKey();
                if (pk.contains(FastConstVal.DOT)) {
                    pk = pk.split(FastConstVal.REG_DOT, 2)[1];
                }
                String finalPk = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, pk);
                Set<Object> condition = new HashSet<>();
                result.forEach(map -> {
                    Object value = map.get(finalPk);
                    if (value != null) condition.add(value);
                });

                if (condition.size() > 0) {
                    List<Map<String, Object>> withResult = iBaseService.
                            select().
                            in(foreignKey, condition).
                            end().
                            listMap();
                    if (withResult != null) {
                        String fk = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, foreignKey.split(FastConstVal.REG_DOT, 2)[1]);
                        result.replaceAll(map -> {
                            Object v = map.get(finalPk);
                            withResult.forEach(obj -> {
                                if (obj.get(fk).equals(v)) map.putAll(obj);
                            });
                            return map;
                        });
                    }
                }
            }
            iBaseServiceList.clear();
        }
        if (fastWrapper != null) fastWrapper.clear();
        return result;
    }

    private List<IBaseService<?>> getIBaseServiceList() {
        if (iBaseServiceList == null) iBaseServiceList = new ArrayList<>();
        return iBaseServiceList;
    }

    @Override
    @SneakyThrows
    public <P> T add(P param){
        T entity = getEntityClass().getDeclaredConstructor().newInstance();
        BeanUtil.copyProperties(param,entity);
        boolean result = save(entity);
        if (!result) throw new HttpException(ApiCode.FAIL_NOT_METHOD.getMsg());
        return entity;
    }


    @Override
    @SneakyThrows
    public <P> void replace(P param) {
        if (fastWrapper == null) throw new HttpException(ApiCode.FAIL_MISSING_ARGUMENT.getMsg());
        T entity = getEntityClass().getDeclaredConstructor().newInstance();
        BeanUtil.copyProperties(param,entity);
        boolean result = update(entity,fastWrapper);
        if (!result) throw new HttpException(ApiCode.FAIL_NOT_METHOD.getMsg());

    }

}
